package com.yuno.mock.util;

import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

/**
 * 进度跟踪器
 * 用于跟踪Excel读取任务的进度
 * 
 * @author yuno
 * @since 2024-01-01
 */
@Slf4j
public class ProgressTracker {

    /**
     * 进度信息
     */
    public static class ProgressInfo {
        private String requestId;
        private String status;
        private long totalRows;
        private AtomicLong processedRows;
        private long totalShards;
        private AtomicLong completedShards;
        private LocalDateTime startTime;
        private LocalDateTime lastUpdateTime;
        private String currentPhase;
        private String message;
        private Double percentage;
        private Long estimatedRemainingTime;
        private Map<String, Object> metadata;

        public ProgressInfo(String requestId) {
            this.requestId = requestId;
            this.processedRows = new AtomicLong(0);
            this.completedShards = new AtomicLong(0);
            this.startTime = LocalDateTime.now();
            this.lastUpdateTime = LocalDateTime.now();
            this.status = "INITIALIZING";
            this.metadata = new ConcurrentHashMap<>();
        }

        // Getters and Setters
        public String getRequestId() { return requestId; }
        public void setRequestId(String requestId) { this.requestId = requestId; }

        public String getStatus() { return status; }
        public void setStatus(String status) { 
            this.status = status; 
            this.lastUpdateTime = LocalDateTime.now();
        }

        public long getTotalRows() { return totalRows; }
        public void setTotalRows(long totalRows) { this.totalRows = totalRows; }

        public long getProcessedRows() { return processedRows.get(); }
        public void setProcessedRows(long processedRows) { 
            this.processedRows.set(processedRows);
            this.lastUpdateTime = LocalDateTime.now();
            updatePercentage();
        }
        public void incrementProcessedRows(long increment) { 
            this.processedRows.addAndGet(increment);
            this.lastUpdateTime = LocalDateTime.now();
            updatePercentage();
        }

        public long getTotalShards() { return totalShards; }
        public void setTotalShards(long totalShards) { this.totalShards = totalShards; }

        public long getCompletedShards() { return completedShards.get(); }
        public void setCompletedShards(long completedShards) { 
            this.completedShards.set(completedShards);
            this.lastUpdateTime = LocalDateTime.now();
        }
        public void incrementCompletedShards() { 
            this.completedShards.incrementAndGet();
            this.lastUpdateTime = LocalDateTime.now();
        }

        public LocalDateTime getStartTime() { return startTime; }
        public void setStartTime(LocalDateTime startTime) { this.startTime = startTime; }

        public LocalDateTime getLastUpdateTime() { return lastUpdateTime; }
        public void setLastUpdateTime(LocalDateTime lastUpdateTime) { this.lastUpdateTime = lastUpdateTime; }

        public String getCurrentPhase() { return currentPhase; }
        public void setCurrentPhase(String currentPhase) { 
            this.currentPhase = currentPhase;
            this.lastUpdateTime = LocalDateTime.now();
        }

        public String getMessage() { return message; }
        public void setMessage(String message) { 
            this.message = message;
            this.lastUpdateTime = LocalDateTime.now();
        }

        public Double getPercentage() { return percentage; }
        public void setPercentage(Double percentage) { this.percentage = percentage; }

        public Long getEstimatedRemainingTime() { return estimatedRemainingTime; }
        public void setEstimatedRemainingTime(Long estimatedRemainingTime) { this.estimatedRemainingTime = estimatedRemainingTime; }

        public Map<String, Object> getMetadata() { return metadata; }
        public void setMetadata(Map<String, Object> metadata) { this.metadata = metadata; }
        public void addMetadata(String key, Object value) { this.metadata.put(key, value); }

        private void updatePercentage() {
            if (totalRows > 0) {
                this.percentage = (double) processedRows.get() / totalRows * 100.0;
                
                // 估算剩余时间
                long elapsedTime = java.time.Duration.between(startTime, LocalDateTime.now()).toMillis();
                if (processedRows.get() > 0 && percentage < 100.0) {
                    long avgTimePerRow = elapsedTime / processedRows.get();
                    long remainingRows = totalRows - processedRows.get();
                    this.estimatedRemainingTime = remainingRows * avgTimePerRow;
                }
            }
        }

        public Map<String, Object> toMap() {
            Map<String, Object> map = new ConcurrentHashMap<>();
            map.put("requestId", requestId);
            map.put("status", status);
            map.put("totalRows", totalRows);
            map.put("processedRows", processedRows.get());
            map.put("totalShards", totalShards);
            map.put("completedShards", completedShards.get());
            map.put("startTime", startTime);
            map.put("lastUpdateTime", lastUpdateTime);
            map.put("currentPhase", currentPhase);
            map.put("message", message);
            map.put("percentage", percentage);
            map.put("estimatedRemainingTime", estimatedRemainingTime);
            map.put("metadata", metadata);
            return map;
        }
    }

    // 静态实例
    private static final ProgressTracker INSTANCE = new ProgressTracker();

    // 进度信息存储
    private final Map<String, ProgressInfo> progressMap = new ConcurrentHashMap<>();
    
    // 进度回调
    private final Map<String, Consumer<ProgressInfo>> callbacks = new ConcurrentHashMap<>();

    private ProgressTracker() {}

    public static ProgressTracker getInstance() {
        return INSTANCE;
    }

    /**
     * 初始化进度跟踪
     */
    public ProgressInfo initProgress(String requestId) {
        ProgressInfo progress = new ProgressInfo(requestId);
        progressMap.put(requestId, progress);
        log.debug("初始化进度跟踪: {}", requestId);
        return progress;
    }

    /**
     * 获取进度信息
     */
    public ProgressInfo getProgress(String requestId) {
        return progressMap.get(requestId);
    }

    /**
     * 获取进度信息Map
     */
    public Map<String, Object> getProgressMap(String requestId) {
        ProgressInfo progress = progressMap.get(requestId);
        return progress != null ? progress.toMap() : null;
    }

    /**
     * 更新进度状态
     */
    public void updateStatus(String requestId, String status) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.setStatus(status);
            notifyCallback(requestId, progress);
            log.debug("更新进度状态: {} -> {}", requestId, status);
        }
    }

    /**
     * 更新当前阶段
     */
    public void updatePhase(String requestId, String phase) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.setCurrentPhase(phase);
            notifyCallback(requestId, progress);
            log.debug("更新当前阶段: {} -> {}", requestId, phase);
        }
    }

    /**
     * 更新消息
     */
    public void updateMessage(String requestId, String message) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.setMessage(message);
            notifyCallback(requestId, progress);
            log.debug("更新消息: {} -> {}", requestId, message);
        }
    }

    /**
     * 设置总行数
     */
    public void setTotalRows(String requestId, long totalRows) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.setTotalRows(totalRows);
            notifyCallback(requestId, progress);
            log.debug("设置总行数: {} -> {}", requestId, totalRows);
        }
    }

    /**
     * 更新已处理行数
     */
    public void updateProcessedRows(String requestId, long processedRows) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.setProcessedRows(processedRows);
            notifyCallback(requestId, progress);
        }
    }

    /**
     * 增加已处理行数
     */
    public void incrementProcessedRows(String requestId, long increment) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.incrementProcessedRows(increment);
            notifyCallback(requestId, progress);
        }
    }

    /**
     * 设置总分片数
     */
    public void setTotalShards(String requestId, long totalShards) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.setTotalShards(totalShards);
            notifyCallback(requestId, progress);
            log.debug("设置总分片数: {} -> {}", requestId, totalShards);
        }
    }

    /**
     * 增加已完成分片数
     */
    public void incrementCompletedShards(String requestId) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.incrementCompletedShards();
            notifyCallback(requestId, progress);
            log.debug("增加已完成分片数: {} -> {}", requestId, progress.getCompletedShards());
        }
    }

    /**
     * 添加元数据
     */
    public void addMetadata(String requestId, String key, Object value) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.addMetadata(key, value);
            notifyCallback(requestId, progress);
        }
    }

    /**
     * 注册进度回调
     */
    public void registerCallback(String requestId, Consumer<ProgressInfo> callback) {
        callbacks.put(requestId, callback);
        log.debug("注册进度回调: {}", requestId);
    }

    /**
     * 移除进度回调
     */
    public void removeCallback(String requestId) {
        callbacks.remove(requestId);
        log.debug("移除进度回调: {}", requestId);
    }

    /**
     * 通知回调
     */
    private void notifyCallback(String requestId, ProgressInfo progress) {
        Consumer<ProgressInfo> callback = callbacks.get(requestId);
        if (callback != null) {
            try {
                callback.accept(progress);
            } catch (Exception e) {
                log.error("进度回调执行失败: {}", requestId, e);
            }
        }
    }

    /**
     * 完成进度跟踪
     */
    public void completeProgress(String requestId, String status, String message) {
        ProgressInfo progress = progressMap.get(requestId);
        if (progress != null) {
            progress.setStatus(status);
            progress.setMessage(message);
            progress.setPercentage(100.0);
            notifyCallback(requestId, progress);
            log.info("完成进度跟踪: {} -> {}", requestId, status);
        }
    }

    /**
     * 清理进度信息
     */
    public void cleanupProgress(String requestId) {
        progressMap.remove(requestId);
        callbacks.remove(requestId);
        log.debug("清理进度信息: {}", requestId);
    }

    /**
     * 获取所有进度信息
     */
    public Map<String, ProgressInfo> getAllProgress() {
        return new ConcurrentHashMap<>(progressMap);
    }

    /**
     * 清理所有进度信息
     */
    public void cleanupAll() {
        progressMap.clear();
        callbacks.clear();
        log.info("清理所有进度信息");
    }
}